//
//  TcpPoller.swift
//  pluto
//
//  Created by andyge on 15/7/30.
//  Copyright © 2015年 gejingguo. All rights reserved.
//

import Foundation

// 外部委托代理协议
public protocol TcpDelegate {
    //func onTcpServerAcceptError(server: TcpServer, err: Error)
    // tcpsever 报错
    func onTcpServerError(server: TcpServer, err: Error)
    // 接受新连接addPoller failed
    func onTcpClinetAddError(server: TcpServer, poller: Poller, err: Error)
    // 客户端连接收到新数据, 返回值为读取数据的字节数量
    func onTcpClientRead(client: TcpClient) -> Int
    // 客户端连接错误
    func onTcpClientError(client: TcpClient, err: Error)
}

public protocol Poller {
    
    var tcpDelegate: TcpDelegate? { get }

    //func initPoller(interval: Int) -> Error?
    func addPollObject(inout po: PollObject) -> Error?
    func removePollObject(po: PollObject) -> Error?
    func updatePollObject(inout po: PollObject, write: Bool) -> Error?
    
    func mainLoop() -> Error?
}

public protocol PollObject {
    var id: Int { get set }
    var fd: Int32? { get }
    func onRead(poller: Poller) -> Error?
    func onWrite(poller: Poller) -> Error?
    func onError(poller: Poller, err: Error)
}

public class KEventPoller: Poller {
    var fd: Int32? = nil
    var stop: Bool = false
    var interval = 0
    public var tcpDelegate: TcpDelegate? = nil
    var ids = [Int]()
    var objects: [PollObject?]? = nil
    
    public init(max: Int, tcpDelegate: TcpDelegate, interval: Int) {
        self.interval = interval
        if self.interval <= 0 {
            self.interval = 10
        }
        fd = kqueue()
        self.tcpDelegate = tcpDelegate
        // free id list
        for var i = max; i > 0; i-- {
            ids.append(i)
        }
        objects = [PollObject?](count: max+1, repeatedValue: nil)
    }
    
    deinit {
        if fd != nil && fd! >= 0 {
            close(fd!)
            fd = nil
        }
    }
    
    
    public func addPollObject(inout po: PollObject) -> Error? {
        if fd == nil {
            return TcpError(err: TcpErrorNo.TcpErrorNo_KqueueNotInit.rawValue, msg: "kqueue fd not init")
        }
        
        if ids.isEmpty {
            return TcpError(err: TcpErrorNo.TcpErrorNo_PollerCountLimit.rawValue, msg: "poller object count limit failed")
        }
        
        let id = ids.removeLast()
        po.id = id
        self.objects![id] = po
        
        // 设置fd非阻塞
        let err = setSocketNonBlocking(po.fd!)
        if err != nil {
            ids.append(id)
            self.objects![id] = nil
            return err
        }
        
        let ret = withUnsafeMutablePointer(&po.id) { ptr -> Int32 in
            var e = kevent()
            e.ident = UInt(po.fd!)
            e.filter = Int16(EVFILT_READ)
            e.flags = UInt16(EV_ADD | EV_ENABLE)
            e.fflags = UInt32(0)
            e.data = 0
            e.udata = UnsafeMutablePointer<Void>(ptr)
            print("add po object e:\(e)")
            return withUnsafePointer(&e) { eptr -> Int32 in
                return Darwin.kevent(fd!, eptr, Int32(1), nil, Int32(0), nil)
            }
            
        }
        
        if ret < 0 {
            ids.append(id)
            self.objects![id] = nil
            return TcpError(err: TcpErrorNo.TcpErrorNo_Kevent.rawValue)
        }
        
        print("poller addobject id:\(po.id) \(po)")
        
        return nil
    }
    
    public func removePollObject(po: PollObject) -> Error? {
        if fd == nil {
            return TcpError(err: TcpErrorNo.TcpErrorNo_KqueueNotInit.rawValue, msg: "kqueue fd not init")
        }
        
        ids.append(po.id)
        let pfd = po.fd!
        let pid = po.id
        objects![po.id] = nil

        var e = kevent()
        e.ident = UInt(pfd)
        e.filter = Int16(EVFILT_READ)
        e.flags = UInt16(EV_DELETE)
        e.fflags = UInt32(0)
        e.data = 0
        e.udata = nil
        
        var ret =  withUnsafePointer(&e) { eptr -> Int32 in
            return Darwin.kevent(fd!, eptr, Int32(1), nil, Int32(0), nil)
        }
        
        if ret < 0 {
            return TcpError(err: TcpErrorNo.TcpErrorNo_Kevent.rawValue)
        }
        
        //var e = kevent()
        e.ident = UInt(pfd)
        e.filter = Int16(EVFILT_WRITE)
        e.flags = UInt16(EV_DELETE)
        e.fflags = UInt32(0)
        e.data = 0
        e.udata = nil
        
        ret =  withUnsafePointer(&e) { eptr -> Int32 in
            return Darwin.kevent(fd!, eptr, Int32(1), nil, Int32(0), nil)
        }
        
        if ret < 0 {
            return TcpError(err: TcpErrorNo.TcpErrorNo_Kevent.rawValue)
        }
        
        print("poller remove object id:\(pid)")

        return nil
    }
    
    public func updatePollObject(inout po: PollObject, write: Bool) -> Error? {
        if fd == nil {
            return TcpError(err: TcpErrorNo.TcpErrorNo_KqueueNotInit.rawValue, msg: "kqueue fd not init")
        }
        
        let ret = withUnsafeMutablePointer(&po.id) { ptr -> Int32 in
            var e = kevent()
            e.ident = UInt(po.fd!)
            e.filter = Int16(EVFILT_WRITE)
            if write {
                e.flags = UInt16(EV_ADD | EV_ENABLE)
            } else {
                e.flags = UInt16(EV_DISABLE)
            }
            e.fflags = UInt32(0)
            e.data = 0
            e.udata = UnsafeMutablePointer<Void>(ptr)
            
            return withUnsafePointer(&e) { eptr -> Int32 in
                return Darwin.kevent(fd!, eptr, Int32(1), nil, Int32(0), nil)
            }
        }
        
        if ret < 0 {
            return TcpError(err: TcpErrorNo.TcpErrorNo_Kevent.rawValue)
        }
        
        print("poller update object id:\(po.id), write:\(write)")

        return nil
    }
    
    public func mainLoop() -> Error? {
        //var timeBeg = timespec();
        let events = UnsafeMutablePointer<kevent>.alloc(1024)
        events.initialize(kevent())
        
        var err: Error? = nil
        while !stop {
           
            var tv = timespec()
            tv.tv_sec = 0
            tv.tv_nsec = 1000000*self.interval
            
            let ret = kevent(fd!, nil, 0, events, 1024, &tv)
            if ret < 0 {
                err = TcpError(err: TcpErrorNo.TcpErrorNo_Kevent.rawValue)
                break
            }
            
            for var i = 0; i < Int(ret); ++i {
                //print(ret)
                let event = events.advancedBy(i).memory
                print("poll mainloop i:\(i) event:\(event)")
                let id = UnsafeMutablePointer<Int>(event.udata).memory
                print("kevent udata id: \(id)")
                let po = self.objects![id]
                if po == nil {
                    print("kevent po nil")
                    continue
                }
                //Int32 e = Int32(events.advancedBy(i).memory.filter)
                if (event.flags & UInt16(EV_ERROR)) != 0 {
                    print("po net err, flags:\(event.flags), err:\(EV_ERROR)")
                    let perr = TcpError(err: TcpErrorNo.TcpErrorNo_Kevent.rawValue)
                    po!.onError(self, err: perr)
                    self.removePollObject(po!)
                    //continue
                } else if event.filter == Int16(EVFILT_READ) {
                    print("po read ready, filter:\(event.filter), read:\(EVFILT_READ)")
                    let perr = po!.onRead(self)
                    if perr != nil {
                        po!.onError(self, err: perr!)
                        self.removePollObject(po!)
                        //continue
                    }
                } else if event.filter == Int16(EVFILT_WRITE) {
                    print("po write ready, filter:\(event.filter), write:\(EVFILT_WRITE)")
                    let perr = po!.onWrite(self)
                    if perr != nil {
                        po!.onError(self, err: perr!)
                        self.removePollObject(po!)
                        //continue
                    }
                }
                
            }
            
            usleep(10000)
        }
        
        events.destroy()
        events.dealloc(1024)
        
        return err
    }
}